﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;

using AloneLibrary.Services.Camera;
using XNAnimation;
using XNAnimation.Controllers;
using XNAnimation.Effects;

namespace AloneLibrary.GameObjects
{
	public class DynamicGameObject : GameObject
	{
        protected SkinnedModel skinnedModel;
        protected AnimationController animationController;
        protected bool disabledForward = false;
        protected bool disabledBackward = false;
        protected bool disabledLeft = false;
        protected bool disabledRight = false;
        protected float forwardSpeed = 10f;

		public Vector3 Velocity = Vector3.Zero;


        public DynamicGameObject(SkinnedModel model, AnimationController animationController, Effect effect, Vector3 position, Vector2 rotation, float scale, Vector3 velocity)
            : base(model.Model, effect, position, rotation, scale)
		{
            this.skinnedModel = model;
            this.animationController = animationController;
            this.Velocity = velocity;
            CreateBoundingSphere();
        }

        public AnimationController AnimationController {
            get { return animationController; }
            set { animationController = value; }
        }

        public SkinnedModel SkinnedModel {
            get { return this.skinnedModel; }
        }

        /// <summary>
        /// Gets or sets the bounding sphere.
        /// </summary>
        /// <value>The bounding sphere.</value>
        public BoundingSphere BoundingSphere //for the dynamic objects
        {
            get
            {
                boundingSphere.Center = Position;
                return boundingSphere;
            }
            set { boundingSphere = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [disabled forward].
        /// </summary>
        /// <value><c>true</c> if [disabled forward]; otherwise, <c>false</c>.</value>
        public bool DisabledForward
        {
            get { return disabledForward; }
            set { disabledForward = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [disabled backward].
        /// </summary>
        /// <value><c>true</c> if [disabled backward]; otherwise, <c>false</c>.</value>
        public bool DisabledBackward
        {
            get { return disabledBackward; }
            set { disabledBackward = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [disabled left].
        /// </summary>
        /// <value><c>true</c> if [disabled left]; otherwise, <c>false</c>.</value>
        public bool DisabledLeft
        {
            get { return disabledLeft; }
            set { disabledLeft = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether [disabled right].
        /// </summary>
        /// <value><c>true</c> if [disabled right]; otherwise, <c>false</c>.</value>
        public bool DisabledRight
        {
            get { return disabledRight; }
            set { disabledRight = value; }
        }

        /// <summary>
        /// Gets or sets the forward speed.
        /// </summary>
        /// <value>The forward speed.</value>
        public float ForwardSpeed
        {
            get { return forwardSpeed; }
            set { forwardSpeed = value; }
        }

        /// <summary>
        /// Creates the bounding sphere.
        /// </summary>
        private void CreateBoundingSphere()
        {
            //all dynamic objects need this
            boundingSphere = new BoundingSphere(Vector3.Zero, 0.0f);

            foreach (ModelMesh mesh in skinnedModel.Model.Meshes)
            {
                BoundingSphere part = mesh.BoundingSphere.Transform(mesh.ParentBone.Transform);
                boundingSphere = BoundingSphere.CreateMerged(boundingSphere, part);
            }
        }

        /*
         * returns a boolean array with true set if it is a colliding-direction.
         * 0 = Up, 1 = Down, 2 = Left, 3 = Right
         */
        public bool[] GetCollisionDirections(StaticGameObject a)
        {
            if (!a.Available)
            {
                bool[] temp = new bool[4];
                temp[0] = true;
                temp[1] = true;
                temp[2] = true;
                temp[3] = true;
                return temp;
            }

            Matrix movement = Matrix.CreateRotationY(this.Rotation.Y);
            Vector3 tmpPosition = this.Position;

            bool[] directions = new bool[4];

            //create next forward position and check it
            Vector3 forwardFactor = new Vector3(0, 0, this.ForwardSpeed);
            forwardFactor = Vector3.Transform(forwardFactor, movement);

            tmpPosition += forwardFactor;

            directions[0] = CheckPosition(tmpPosition, a) ? true : false;

            //reset
            tmpPosition = this.Position;

            //create next backward position and check it
            Vector3 backwardFactor = new Vector3(0, 0, this.ForwardSpeed * -1);
            backwardFactor = Vector3.Transform(backwardFactor, movement);

			tmpPosition += backwardFactor;

            directions[1] = CheckPosition(tmpPosition, a) ? true : false;

            //reset
            tmpPosition = this.Position;

            //create next left position and check it
            Vector3 leftFactor = new Vector3(this.ForwardSpeed, 0, 0);
            leftFactor = Vector3.Transform(leftFactor, movement);

            tmpPosition += leftFactor;

            directions[2] = CheckPosition(tmpPosition, a) ? true : false;

            //reset
            tmpPosition = this.Position;

            //create next right position and check it
            Vector3 rightFactor = new Vector3(-this.ForwardSpeed, 0, 0);
            rightFactor = Vector3.Transform(rightFactor, movement);

            tmpPosition += rightFactor;

            directions[3] = CheckPosition(tmpPosition, a) ? true : false;

            return directions;
        }

        /// <summary>
        /// Checks the position.
        /// </summary>
        /// <param name="a">A.</param>
        /// <param name="b">The b.</param>
        /// <returns></returns>
        public bool CheckPosition(Vector3 a, StaticGameObject b)
        {
            if (!b.Available) return false;

            BoundingBox bb = b.BoundingBox;

            if ((a.X >= bb.Min.X && a.X <= bb.Max.X) &&
                (a.Y >= bb.Min.Y && a.Y <= bb.Max.Y) &&
                (a.Z >= bb.Min.Z && a.Z <= bb.Max.Z)) return true;

            return false;
        }

        /// <summary>
        /// Determines whether the specified b is colliding.
        /// </summary>
        /// <param name="b">The b.</param>
        /// <returns>
        /// 	<c>true</c> if the specified b is colliding; otherwise, <c>false</c>.
        /// </returns>
        public bool isColliding(DynamicGameObject b)
        {
            /*
             * Start by checking the large boundingSpheres, and
             * if they intersect, check the small ones
             */
            if (b.Available && this.BoundingSphere.Intersects(b.BoundingSphere))
            {
                foreach (ModelMesh meshA in this.SkinnedModel.Model.Meshes)
                {
                    foreach (ModelMesh meshB in b.SkinnedModel.Model.Meshes)
                    {
                        if (meshB.BoundingSphere.Intersects(meshA.BoundingSphere))
                            return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Determines whether the specified b is colliding.
        /// </summary>
        /// <param name="b">The b.</param>
        /// <returns>
        /// 	<c>true</c> if the specified b is colliding; otherwise, <c>false</c>.
        /// </returns>
        public bool isColliding(StaticGameObject b)
        {
            if (b.Available && this.BoundingSphere.Intersects(b.BoundingBox)) //check large first
            {
                BoundingSphere tmp;

                foreach (ModelMesh mesh in SkinnedModel.Model.Meshes) //check the details
                {
                    tmp = mesh.BoundingSphere; //seems like this is nescessary because it's not accessible for change otherwise
                    tmp.Center += Position;

                    if (tmp.Intersects(b.BoundingBox))
                        return true;
                }
            }
            return false;
        }

        public bool isCollidingRay(Ray ray)
        {
            if (ray.Intersects(this.BoundingSphere) != null) //check large first
            {
                BoundingSphere tmp;

                foreach (ModelMesh mesh in this.SkinnedModel.Model.Meshes) //check the details
                {
                    tmp = mesh.BoundingSphere; //seems like this is nescessary because it's not accessible for change otherwise
                    tmp.Center += Position;

                    if(ray.Intersects(tmp) != null) return true;
                }
            }
            return false;
        }
	}
}
